﻿using gt.rediscache.core.Connections.ClientServer;
using gt.rediscache.core.Entry;
using gt.rediscache.core.Utility;
using StackExchange.Redis;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace gt.rediscache.core.Clients
{
    public partial class RedisClient : IRedisClient
    {
        private RedisClientServerPool _clientServerPool = null;
        private RedisClientConfiguration _configuration = null;

        #region Construct

        public RedisClient(RedisClientConfiguration options, RedisClientServerPoolFactory poolFactory)
        {
            _configuration = options ?? throw new ArgumentNullException(nameof(options));
            if (poolFactory == null) throw new ArgumentNullException(nameof(RedisClientServerPoolFactory));
            if (!CheckOptions(options)) throw new InvalidOperationException("RedisClientOptions property value error!");

            _clientServerPool = poolFactory.GetOrCreate(_configuration.Pool);
            _clientServerPool.OnClientServerChanged += (sender, args) => HandleOnClientServerChanged(sender as RedisClientServer, args.Health);
        }

        #endregion

        /// <summary>
        /// redis 连接池
        /// </summary>
        protected RedisClientServerPool ClientServerPool => _clientServerPool;
        /// <summary>
        ///  do something when clientserver changed event
        /// </summary>
        protected virtual void HandleOnClientServerChanged(RedisClientServer clientServer, bool health)
        {
        }
        /// <summary>
        /// 执行Redis命令
        /// </summary>
        /// <param name="command">redis命令</param>
        /// <param name="message">message</param>
        /// <param name="commandFlags">commandFlags</param>
        /// <returns></returns>
        protected virtual async Task<EMRedisResult> ExecuteAsync(RedisCommand command, RedisMessage message, CommandFlags commandFlags = CommandFlags.None)
        {
            var clientServer = GetClientServer(message.Key);
            return await clientServer.ExecuteCommandAsync(command, message, commandFlags).ConfigureAwait(false);
        }
        /// <summary>
        /// 异步执行Redis命令
        /// </summary>
        /// <param name="command">redis命令</param>
        /// <param name="message">message</param>
        /// <param name="commandFlags">commandFlags</param>
        /// <returns></returns>
        protected virtual EMRedisResult Execute(RedisCommand command, RedisMessage message, CommandFlags commandFlags = CommandFlags.None)
        {
            var clientServer = GetClientServer(message.Key);
            return clientServer.ExecuteCommand(command, message, commandFlags);
        }
        /// <summary>
        /// Options检查
        /// </summary>
        protected virtual bool CheckOptions(RedisClientConfiguration Options)
        {
            return !string.IsNullOrEmpty(Options.ClientName)
                && Options.Pool != null
                && !string.IsNullOrEmpty(Options.Pool.Name);
        }

        #region Server

        /// <summary>
        /// 缓存名称
        /// </summary>
        public string AppCacheName { get { return _configuration.ClientName; } }
        /// <summary>
        /// 备 缓存客户端
        /// </summary>
        public virtual IRedisClient BackupClient { get { return null; } }

        public virtual RedisClientConfiguration Options => _configuration;

        /// <summary>
        /// 检查clientserver是否健康
        /// </summary>
        /// <returns></returns>
        public bool CheckHealth(RedisClientServer clientServer)
        {
            return _clientServerPool.CheckHealth(clientServer.Name);
        }
        /// <summary>
        /// 获取RedisClientServer
        /// </summary>
        /// <returns></returns>
        public RedisClientServer GetClientServerByNodeName(string nodeName)
        {
            return _clientServerPool.GetClientServerByNodeName(nodeName);
        }
        /// <summary>
        /// 获取RedisClientServer
        /// </summary>
        /// <returns></returns>
        public RedisClientServer[] GetClientServers()
        {
            return _clientServerPool.GetAllRedisClientServer().ToArray();
        }
        /// <summary>
        /// 获取RedisClientServer
        /// </summary>
        /// <returns></returns>
        public RedisClientServer GetClientServer(string key)
        {
            return _clientServerPool.GetClientServer(key);
        }
        /// <summary>
        /// 获取RedisClientServer
        /// item1=clientServer item2=是否发生switch
        /// </summary>
        /// <returns></returns>
        public virtual Tuple<RedisClientServer, bool> GetSmartClientServer(string key)
        {
            return new Tuple<RedisClientServer, bool>(GetClientServer(key), false);
        }
        /// <summary>
        /// 获取缓存客户端信息
        /// </summary>
        /// <returns></returns>
        public virtual RedisClientInfo RedisCacheInfo()
        {
            RedisClientInfo info = new RedisClientInfo()
            {
                Version = RedisClientUtility.Version,
                ApplicatioinName = _configuration.ApplicationName,
                ServerIp = RedisClientUtility.GetLocalIp(),
                IDC = _configuration.IDC,
                AppCacheName = _configuration.ClientName,
                ConnectionCache = new RedisConnectCache
                {
                    Name = _configuration.Pool.Name,
                    HasActive = true,
                    Nodes = GetClientServers().Select((cs) =>
                    {
                        return new RedisConnectNode
                        {
                            DB = cs.Node.DB,
                            Name = cs.Node.Name,
                            SlotFrom = cs.Node.SlotFrom,
                            SlotTo = cs.Node.SlotTo,
                            Master = cs.Node.Master,
                            Slaves = cs.Node.Slaves,
                            Available = true
                        };
                    }).ToList()
                }
            };
            return info;
        }

        #endregion

        #region Key

        /// <summary>
        /// 获取分布式锁
        /// </summary>
        /// <returns></returns>
        public bool LockTake(string key, string value, TimeSpan expiredTime)
        {
            var message = new LockTakeMessage(key, value, expiredTime);
            return Execute(RedisCommand.LockTake, message, CommandFlags.DemandMaster).BoolResult;
        }
        /// <summary>
        /// 获取分布式锁value
        /// </summary>
        /// <returns></returns>
        public string LockQuery(string key)
        {
            var message = new LockQueryMessage(key);
            return Execute(RedisCommand.LockQuery, message, CommandFlags.DemandMaster).RedisValueResult;
        }
        /// <summary>
        /// 释放分布式锁
        /// </summary>
        /// <returns></returns>
        public bool LockRelease(string key, string value)
        {
            var message = new LockReleaseMessage(key, value);
            return Execute(RedisCommand.LockRelease, message, CommandFlags.DemandMaster).BoolResult;
        }
        /// <summary>
        /// 获取Redis信息
        /// </summary>
        /// <param name="hostAndport"></param>
        /// <returns></returns>
        public RedisInfo RedisInfo(string hostAndport)
        {
            var cs = GetClientServers().FirstOrDefault(e => string.Equals(e.Node.Master, hostAndport) || e.Node.Slaves.Contains(hostAndport));
            if (cs == null) return null;
            var infoString = cs.ExecuteCommand(RedisCommand.Info, new RedisInfoMessage(hostAndport), CommandFlags.DemandMaster).InfoResult;
            return RedisInfoParser.FromInfoString(infoString);
        }
        /// <summary>
        /// 获取Redis连接信息
        /// </summary>
        /// <returns></returns>
        public ConnectClientInfo[] RedisClients(string hostAndport)
        {
            var cs = GetClientServers().FirstOrDefault(e => string.Equals(e.Node.Master, hostAndport) || e.Node.Slaves.Contains(hostAndport));
            if (cs == null) return null;
            var clientList = cs.ExecuteCommand(RedisCommand.ClientList, new ClientListMessage(hostAndport), CommandFlags.DemandMaster).ClientInfoArrayResult;
            return RedisInfoParser.FromClientInfo(clientList);
        }

        public bool KeyExists(string key, CommandFlags flags = CommandFlags.None)
        {
            var message = new KeyExistsMessage(key);
            return Execute(RedisCommand.KeyExists, message, flags).BoolResult;
        }

        public bool KeyRemove(string key, CommandFlags flags = CommandFlags.None)
        {
            var message = new KeyRemoveMessage(key);
            return Execute(RedisCommand.KeyRemove, message, flags).BoolResult;
        }

        public long KeyRemove(string[] keys, CommandFlags flags = CommandFlags.None)
        {
            if (_clientServerPool.Mode != PoolMode.Single) throw new InvalidOperationException("arrary key remove not support in cluster mode!");
            var redisKeys = RedisValueConverter.ConvertToRedisKeyFromString(keys);
            if (redisKeys == null || redisKeys.Length == 0) return 0;
            var message = new KeyRemoveArrayMessage(redisKeys);
            return Execute(RedisCommand.KeyRemoveArray, message, flags).LongResult;
        }

        public bool KeyExpired(string key, TimeSpan expiredTime, CommandFlags flags = CommandFlags.None)
        {
            var message = new KeyExpiredMessage(key, expiredTime);
            return Execute(RedisCommand.KeyExpired, message, flags).BoolResult;
        }

        public TimeSpan? KeyTimeToLive(string key, CommandFlags flags = CommandFlags.None)
        {
            var message = new KeyTimeToLiveMessage(key);
            return Execute(RedisCommand.KeyTimeToLive, message, flags).TimeSpanResult;
        }

        public async Task<bool> KeyExistsAsync(string key, CommandFlags flags = CommandFlags.None)
        {
            var message = new KeyExistsMessage(key);
            return await ExecuteAsync(RedisCommand.KeyExists, message, flags).ContinueWith(t =>
            {
                return t.Result.BoolResult;
            }).ConfigureAwait(false);
        }

        public async Task<bool> KeyRemoveAsync(string key, CommandFlags flags = CommandFlags.None)
        {
            var message = new KeyRemoveMessage(key);
            return await ExecuteAsync(RedisCommand.KeyRemove, message, flags).ContinueWith(t =>
            {
                return t.Result.BoolResult;
            }).ConfigureAwait(false);
        }

        public async Task<bool> KeyExpiredAsync(string key, TimeSpan expiredTime, CommandFlags flags = CommandFlags.None)
        {
            var message = new KeyExpiredMessage(key, expiredTime);
            return await ExecuteAsync(RedisCommand.KeyExpired, message, flags).ContinueWith(t =>
            {
                return t.Result.BoolResult;
            }).ConfigureAwait(false);
        }

        public async Task<TimeSpan?> KeyTimeToLiveAsync(string key, CommandFlags flags = CommandFlags.None)
        {
            var message = new KeyTimeToLiveMessage(key);
            return await ExecuteAsync(RedisCommand.KeyTimeToLive, message, flags).ContinueWith(t =>
            {
                return t.Result.TimeSpanResult;
            }).ConfigureAwait(false);
        }

        #endregion

        #region Dispose

        /// <summary>
        /// 通常 不建议调用。
        /// IRedisClient应当是常驻的，减少创建连接的消耗
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                _clientServerPool.Dispose();
            }
        }

        #endregion
    }
}
